/*
 * Copyright (c) [2021] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "aarch64_alignment.h"
#include "insn.h"
#include "loop.h"

namespace maplebe {
void AArch64AlignAnalysis::FindLoopHeader() {
  MapleVector<CGFuncLoops*> loops = aarFunc->GetLoops();
  if (loops.empty()) {
    return;
  }
  for (const auto *loop : loops) {
    const BB *header = loop->GetHeader();
    if (header == nullptr) {
      continue;
    }
    InsertLoopHeaderBBs(const_cast<BB &>(*header));
  }
}

void AArch64AlignAnalysis::FindJumpTarget() {
  MapleUnorderedMap<LabelIdx, BB*> label2BBMap = aarFunc->GetLab2BBMap();
  if (label2BBMap.empty()) {
    return;
  }
  for (auto iter = label2BBMap.begin(); iter != label2BBMap.end(); ++iter) {
    BB *jumpBB = iter->second;
    if (jumpBB == nullptr) {
      continue;
    }
    InsertJumpTargetBBs(*jumpBB);
  }
}

bool AArch64AlignAnalysis::IsIncludeCall(BB &bb) {
  return bb.HasCall();
}

bool AArch64AlignAnalysis::IsInSizeRange(BB &bb) {
  uint64 size = 0;
  FOR_BB_INSNS_CONST(insn, &bb) {
    if (!insn->IsMachineInstruction() || insn->GetMachineOpcode() == MOP_pseudo_ret_int ||
        insn->GetMachineOpcode() == MOP_pseudo_ret_float) {
      continue;
    }
    size += kAlignInsnLength;
  }
  BB *curBB = &bb;
  while (curBB->GetNext() != nullptr && curBB->GetNext()->GetLabIdx() == 0) {
    FOR_BB_INSNS_CONST(insn, curBB->GetNext()) {
      if (!insn->IsMachineInstruction() || insn->GetMachineOpcode() == MOP_pseudo_ret_int ||
          insn->GetMachineOpcode() == MOP_pseudo_ret_float) {
        continue;
      }
      size += kAlignInsnLength;
    }
    curBB = curBB->GetNext();
  }
  if (size <= kAlignMinBBSize || size >= kAlignMaxBBSize) {
    return false;
  }
  return true;
}

bool AArch64AlignAnalysis::HasFallthruEdge(BB &bb) {
  for (auto iter = bb.GetPredsBegin(); iter != bb.GetPredsEnd(); ++iter) {
    if (*iter == bb.GetPrev()) {
      return true;
    }
  }
  return false;
}

void AArch64AlignAnalysis::ComputeLoopAlign() {
  if (loopHeaderBBs.empty()) {
    return;
  }
  for (BB *bb : loopHeaderBBs) {
    if (bb == cgFunc->GetFirstBB() || IsIncludeCall(*bb) || !IsInSizeRange(*bb)) {
      continue;
    }
    bb->SetNeedAlign(true);
    AlignInfo targetInfo;
    if (alignInfos.find(bb) == alignInfos.end()) {
      alignInfos[bb] = targetInfo.loopAlign;
    } else {
      uint32 curPower = alignInfos[bb];
      alignInfos[bb] = (targetInfo.loopAlign < curPower) ? targetInfo.loopAlign : curPower;
    }
    bb->SetAlignPower(alignInfos[bb]);
  }
}

void AArch64AlignAnalysis::ComputeJumpAlign() {
  if (jumpTargetBBs.empty()) {
    return;
  }
  for (BB *bb : jumpTargetBBs) {
    if (bb == cgFunc->GetFirstBB() || !IsInSizeRange(*bb) || HasFallthruEdge(*bb)) {
      continue;
    }
    bb->SetNeedAlign(true);
    AlignInfo targetInfo;
    if (alignInfos.find(bb) == alignInfos.end()) {
      alignInfos[bb] = targetInfo.jumpAlign;
    } else {
      uint32 curPower = alignInfos[bb];
      alignInfos[bb] = (targetInfo.jumpAlign < curPower) ? targetInfo.jumpAlign : curPower;
    }
    bb->SetAlignPower(alignInfos[bb]);
  }
}
} /* namespace maplebe */


